﻿using System;
using System.Linq;
using Firestarter.Test.Base;
using FireStarterModeller.BO;
using FireStarterModeller.Logic;
using Habanero.Base.Exceptions;
using Habanero.BO;
using Habanero.BO.Loaders;
using Microsoft.Practices.Unity;
using NUnit.Framework;

namespace FireStarterModeller.Test.Logic
{
    [TestFixture]
    public class TestClassDefToNewSolutionImporter
    {

        [SetUp]
        public void SetupTest()
        {
            //Runs every time that any testmethod is executed
            TestUtilsDomain.SetupTest();
            BORegistry.DataAccessor = new DataAccessorInMemory();
        }

        [TestFixtureSetUp]
        public void TestFixtureSetup()
        {
            //Code that is executed before any test is run in this class. If multiple tests
            // are executed then it will still only be called once.
            TestUtilsDomain.SetupFixture();
        }

        [TearDown]
        public void TearDownTest()
        {
            //runs every time any testmethod is complete
        }

        [Test]
        public virtual void Test_Construct()
        {
            //---------------Set up test pack-------------------

            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            FMDataStore.Reset();
            IUnityContainer container = new AutoMockingUnityContainer();
            string filename = "will not be needed as the spy will autoload the classdef";
            var classDefToSolutionImporter = new ClassDefToNewSolutionImporterSpy(container, filename);
            //---------------Test Result -----------------------
            Assert.IsNotNull(classDefToSolutionImporter);
        }

        [Test]
        public void Test_Import_WhenCalled_ShouldReturnSolution()
        {
            //---------------Set up test pack-------------------
            var classDefToSolutionImporter = CreateClassDefToSolutionImporter();            
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            DMSolution dmSolution = classDefToSolutionImporter.Import();
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmSolution);
        }

        [Test]
        public void Test_Import_WhenCalled_ReturnedSolutionShouldHaveImportedClassDef()
        {
            //---------------Set up test pack-------------------
            var classDefToSolutionImporter = CreateClassDefToSolutionImporter();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            DMSolution dmSolution = classDefToSolutionImporter.Import();
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmSolution);
            Assert.AreEqual("Replace_it.BO", dmSolution.DefaultAssembly.AssemblyName);
        }

        [Test]
        public void Test_ExtractSolutionNameFromAssemblyName_WhenGivenAssmblyNameWithNoFullStop_ShouldReturnSolutionNameEqualToAssemblyName()
        {
            // The solution name should be the rootnamespace of an assembly name
            // i.e. the text prior to the first "." 
            // if there is no "." then the solution name should be the same as the assembly name
            
            //---------------Set up test pack-------------------
            string assemblyName = TestUtilsShared.GetRandomString();

            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string solutionName = ClassDefToSolutionImporter.ExtractSolutionNameFromAssemblyName(assemblyName);

            //---------------Test Result -----------------------
            Assert.AreEqual(assemblyName, solutionName);
        }


        [Test]
        public void Test_ExtractSolutionNameFromAssemblyName_WhenGivenAssmblyNameWithOneFullStop_ShouldReturnSolutionNameEqualToAssemblyName()
        {
            // The solution name should be the rootnamespace of an assembly name
            // i.e. the text prior to the first "." 
            // if there is no "." then the solution name should be the same as the assembly name

            //---------------Set up test pack-------------------
            string assemblyName;
            string assemblyNameRoot = GetAssemblyNameRoot(1, out assemblyName);
            //---------------Assert Precondition----------------

            Assert.AreEqual(1, assemblyName.Count(x => x == '.'));          

            //---------------Execute Test ----------------------
            string solutionName = ClassDefToSolutionImporter.ExtractSolutionNameFromAssemblyName(assemblyName);

            //---------------Test Result -----------------------
            Assert.AreEqual(assemblyNameRoot, solutionName);
        }

        [Test]
        public void Test_ExtractSolutionNameFromAssemblyName_WhenGivenAssmblyNameWithManyFullStops_ShouldReturnSolutionNameEqualToAssemblyName()
        {
            // The solution name should be the rootnamespace of an assembly name
            // i.e. the text prior to the first "." 
            // if there is no "." then the solution name should be the same as the assembly name

            //---------------Set up test pack-------------------
            string assemblyName;
            string assemblyNameRoot = GetAssemblyNameRoot(3, out assemblyName);
            //---------------Assert Precondition----------------

            Assert.AreEqual(3, assemblyName.Count(x => x == '.'));

            //---------------Execute Test ----------------------
            string solutionName = ClassDefToSolutionImporter.ExtractSolutionNameFromAssemblyName(assemblyName);

            //---------------Test Result -----------------------
            Assert.AreEqual(assemblyNameRoot, solutionName);
        }

        [Test]
        public void Test_UpdateSolutionNames_WhenDefaultAssembly_IsNull_ShoudldThrowException_()
        {
            //---------------Set up test pack-------------------
            var classDefToSolutionImporter = CreateClassDefToSolutionImporter();
            classDefToSolutionImporter.CallTempSolutionGetter.DefaultAssembly = null;
            //---------------Assert Precondition----------------
            Assert.IsNull(classDefToSolutionImporter.CallTempSolutionGetter.DefaultAssembly);
            //---------------Execute Test ----------------------
            try
            {
                classDefToSolutionImporter.CallUpdateSolutionNames();
               
                //---------------Test Result -----------------------
                Assert.Fail("Expected to throw an EXCEPTION");
            } 
            catch (Exception ex)
            {
                Assert.IsInstanceOf<HabaneroDeveloperException>(ex);
                StringAssert.Contains("At least one assembly was expected", ex.Message);
            }
            //---------------Test Result -----------------------
        }


        [Test]
        public void Test_Import_WhenTempSolutionNameIs_NewSolution_SolutionNameShouldBeSetToTheDefaultAssmbleyRootNameSpace()
        {
            //---------------Set up test pack-------------------
            var classDefToSolutionImporter = CreateClassDefToSolutionImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(DMSolution.DEFAULT_NEW_SOLUTION_NAME, classDefToSolutionImporter.CallTempSolutionGetter.SolutionName);
            //---------------Execute Test ----------------------
            DMSolution dmSolution = classDefToSolutionImporter.Import();
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmSolution);
            Assert.AreEqual("Replace_it.BO", dmSolution.DefaultAssembly.AssemblyName);
            Assert.AreEqual("Replace_it", dmSolution.SolutionName);
        }

        [Test]
        public void Test_Import_WhenTempSolutionNameIsNot_NewSolutionOrEmptyString_SolutionNameShouldRemainTheSame()
        {
            //---------------Set up test pack-------------------
            var classDefToSolutionImporter = CreateClassDefToSolutionImporter();
            //---------------Assert Precondition----------------
            string expectedSolutionName = TestUtilsShared.GetRandomString();
            classDefToSolutionImporter.CallTempSolutionGetter.SolutionName = expectedSolutionName;
            Assert.AreEqual(expectedSolutionName, classDefToSolutionImporter.CallTempSolutionGetter.SolutionName);
            //---------------Execute Test ----------------------
            DMSolution dmSolution = classDefToSolutionImporter.Import();
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmSolution);
            Assert.AreEqual("Replace_it.BO", dmSolution.DefaultAssembly.AssemblyName);
            Assert.AreEqual(expectedSolutionName, dmSolution.SolutionName);
        }

        [Test]
        public void Test_Import_WhenTempSolutionNameIs_EmptyString_SolutionNameShouldBeSetToTheDefaultAssmbleyRootNameSpace()
        {
            //---------------Set up test pack-------------------
            var classDefToSolutionImporter = CreateClassDefToSolutionImporter();
            classDefToSolutionImporter.CallTempSolutionGetter.SolutionName = "";
            //---------------Assert Precondition----------------
            Assert.IsNullOrEmpty(classDefToSolutionImporter.CallTempSolutionGetter.SolutionName);
            //---------------Execute Test ----------------------
            DMSolution dmSolution = classDefToSolutionImporter.Import();
            //---------------Test Result -----------------------
            Assert.IsNotNull(dmSolution);
            Assert.AreEqual("Replace_it.BO", dmSolution.DefaultAssembly.AssemblyName);
            Assert.AreEqual("Replace_it", dmSolution.SolutionName);
        }

        [Test]
        public void Test_OnProgressChange_WhenCalledWith_ReturnsEventArgsWithProgressPercentage()
        {
            //---------------Set up test pack-------------------
            var classDefToSolutionImporter = CreateClassDefToSolutionImporter();
            int percentage = TestUtilsShared.GetRandomInt(1,100);
            int expected = 0;
            classDefToSolutionImporter.ImportProgressChanged += delegate(object sender, ClassDefToSolutionImportProgressEventArgs e)
            { expected = e.ProgressPercentage; };
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(expected,percentage);
            
            //---------------Execute Test ----------------------
            classDefToSolutionImporter.CallOnImportProgressChanged(percentage);
            //---------------Test Result -----------------------
            Assert.AreEqual(expected, percentage);
        }

        private static ClassDefToNewSolutionImporterSpy CreateClassDefToSolutionImporter()
        {
            FMDataStore.Reset();
            IUnityContainer container = new AutoMockingUnityContainer();
            string filename = "will not be needed as the spy will autoload the classdef";
            return new ClassDefToNewSolutionImporterSpy(container, filename);
        }


        private static string GetAssemblyNameRoot(int numberOfFullStops, out string assemblyName)
        {
            string assemblyNameRoot = TestUtilsShared.GetRandomString();
            assemblyName = assemblyNameRoot;
            for (var i = 0; i < numberOfFullStops; i++)
            {
                assemblyName += "." + TestUtilsShared.GetRandomString();
            }

            return assemblyNameRoot;
        }
    }

    public class ClassDefToNewSolutionImporterSpy : ClassDefToNewSolutionImporter
    {
        public ClassDefToNewSolutionImporterSpy(IUnityContainer iocContainer, string filename) : base(iocContainer, filename)
        {
        }

        public DMSolution CallTempSolutionGetter
        {
            get { return TempSolution; }
        }

        public void CallOnImportProgressChanged(int progressPercentage)
        {
            OnImportProgressChanged(progressPercentage);
        }

        public void CallUpdateSolutionNames()
        {
            UpdateSolutionNames(TempSolution);
        }

        protected override void LoadClassDefsToSolutionForImport(DMSolution dmSolution)
        {
            XmlClassDefsLoader loader = new XmlClassDefsLoader("", new DtdLoader(),
                                                               new FMDefClassFactory(dmSolution));
                loader.LoadClassDefs(@"
				                    <classes>
                                          <class name=""ComputerPart"" assembly=""Replace_it.BO"" displayName=""Computer Part"">
                                            <property name=""ComputerPartID"" type=""Guid"" readWriteRule=""WriteNew"" compulsory=""true"" />
                                            <property name=""Description"">
                                              <rule name=""Description"">
                                                <add key=""maxLength"" value=""500"" />
                                              </rule>
                                            </property>
                                            <primaryKey>
                                              <prop name=""ComputerPartID"" />
                                            </primaryKey>
                                          </class>
                                        </classes>");
        }
    }

}
